﻿using libzkfpcsharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace LD.ZKT
{
    internal class ZKTHelper : IDisposable
    {
        public delegate void ImageHandle(Bitmap bitmap);
        public event ImageHandle ImageChangedEvent;
        public delegate void MessageChangeHandle(string message);
        public event MessageChangeHandle MessageChangeEvent;
        private const int REGISTER_FINGER_COUNT = 1;
        private List<int> driverList = new List<int>();
        private bool init = false;
        private bool open = false;
        private IntPtr mDevHandle = IntPtr.Zero;
        private IntPtr mDBHandle = IntPtr.Zero;
        private int mfpWidth = 0;
        private int mfpHeight = 0;
        private byte[] FPBuffer;
        private bool bIsTimeToDie = false;
        private int cbCapTmp = 2048;
        private byte[][] RegTmps = new byte[3][];
        private byte[] CapTmp = new byte[2048];
        private const int MESSAGE_CAPTURED_OK = 0x0400 + 6;
        private int RegisterCount = 0;
        private bool IsRegister = false;
        private int cbRegTmp = 0;
        private bool bIdentify = true;

        [DllImport("user32.dll", EntryPoint = "SendMessageA")]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        public IntPtr FormHandle = IntPtr.Zero;
        private static ZKTHelper instance;
        private static object locker = new object();
        public static ZKTHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (locker)
                    {
                        if (instance == null)
                        {
                            instance = new ZKTHelper();
                        }
                    }
                }
                return instance;
            }
        }
        public ZKTHelper()
        {

        }
        private string Message
        {
            set { MessageChangeEvent?.Invoke(value); }
        }
        public string TouchId { get; private set; }
        public int Fid { get; set; }
        public void Init()
        {
            int ret = zkfperrdef.ZKFP_ERR_OK;
            if ((ret = zkfp2.Init()) == zkfperrdef.ZKFP_ERR_OK)
            {
                int nCount = zkfp2.GetDeviceCount();
                if (nCount > 0)
                {
                    for (int i = 0; i < nCount; i++)
                    {
                        driverList.Add(i);
                    }

                    init = true;
                }
                else
                {
                    zkfp2.Terminate();
                    MessageBox.Show("指纹识别模块无法找到");
                }
            }
        }

        public void Open()
        {
            if (!init) return;

            int ret = zkfp.ZKFP_ERR_OK;
            if (IntPtr.Zero == (mDevHandle = zkfp2.OpenDevice(driverList[0])))
            {
                MessageBox.Show("指纹识别模块打开失败");
                return;
            }
            if (IntPtr.Zero == (mDBHandle = zkfp2.DBInit()))
            {
                MessageBox.Show("指纹模块初始化失败");
                zkfp2.CloseDevice(mDevHandle);
                mDevHandle = IntPtr.Zero;
                return;
            }

            //register
            var touchs = DBHelper.Instance.Sql.Queryable<TB_UserTouch>()
                        .ToList();

            foreach (var item in touchs)
            {
                if (!string.IsNullOrEmpty(item.TouchId))
                {
                    //int fid = 0, score = 0;
                    byte[] blob = zkfp2.Base64ToBlob(item.TouchId);
                    ret = zkfp2.DBAdd(mDBHandle, item.Id, blob);
                    //ret = zkfp2.DBIdentify(mDBHandle, blob, ref fid, ref score);
                    if (zkfp.ZKFP_ERR_OK == ret)
                    {
                        Console.WriteLine("指纹信息注册成功");
                    }
                }
            }

            RegisterCount = 0;
            cbRegTmp = 0;
            for (int i = 0; i < 3; i++)
            {
                RegTmps[i] = new byte[2048];
            }
            byte[] paramValue = new byte[4];
            int size = 4;
            zkfp2.GetParameters(mDevHandle, 1, paramValue, ref size);
            zkfp2.ByteArray2Int(paramValue, ref mfpWidth);

            size = 4;
            zkfp2.GetParameters(mDevHandle, 2, paramValue, ref size);
            zkfp2.ByteArray2Int(paramValue, ref mfpHeight);
            FPBuffer = new byte[mfpWidth * mfpHeight];
            open = true;

            Thread captureThread = new Thread(new ThreadStart(DoCapture));
            captureThread.IsBackground = true;
            captureThread.Start();
            bIsTimeToDie = false;
        }

        private void register()
        {
            byte[] RegTmp = new byte[2048];

            if (IsRegister)
            {
                int ret = zkfp.ZKFP_ERR_OK;
                int fid = 0, score = 0;
                ret = zkfp2.DBIdentify(mDBHandle, CapTmp, ref fid, ref score);
                if (zkfp.ZKFP_ERR_OK == ret)
                {
                    Message = $"指纹信息已注册{fid}!";
                    return;
                }
                if (RegisterCount > 0 && zkfp2.DBMatch(mDBHandle, CapTmp, RegTmps[RegisterCount - 1]) <= 0)
                {
                    Message = $"请同一手指按三次注册";
                    return;
                }
                Array.Copy(CapTmp, RegTmps[RegisterCount], cbCapTmp);
                String strBase64 = zkfp2.BlobToBase64(CapTmp, cbCapTmp);
                TouchId = strBase64;
                byte[] blob = zkfp2.Base64ToBlob(strBase64);
                RegisterCount++;
                if (RegisterCount >= REGISTER_FINGER_COUNT)
                {
                    RegisterCount = 0;
                    if (zkfp.ZKFP_ERR_OK == (ret = zkfp2.DBMerge(mDBHandle, RegTmps[0], RegTmps[1], RegTmps[2], RegTmp, ref cbRegTmp)) &&
                           zkfp.ZKFP_ERR_OK == (ret = zkfp2.DBAdd(mDBHandle, Fid, RegTmp)))
                    {
                        Fid++;
                        Message = $"注册成功";
                        IsRegister = false;
                        return;
                    }
                    else
                    {
                        Message = $"注册失败,error code={ret}";
                    }
                    IsRegister = false;
                    return;
                }
                else
                {
                    Message = $"你需要按下第{(REGISTER_FINGER_COUNT - RegisterCount)}次注册";
                }
            }
        }
        private void registerOne()
        {
            if (IsRegister)
            {
                int ret = zkfp.ZKFP_ERR_OK;
                int fid = 0, score = 0;

                ret = zkfp2.DBIdentify(mDBHandle, CapTmp, ref fid, ref score);
                if (zkfp.ZKFP_ERR_OK == ret)
                {
                    Message = $"指纹信息已注册{fid}!";
                    return;
                }

                Array.Copy(CapTmp, RegTmps[RegisterCount], cbCapTmp);
                string strBase64 = zkfp2.BlobToBase64(CapTmp, cbCapTmp);
                TouchId = strBase64;
                byte[] blob = zkfp2.Base64ToBlob(strBase64);

                if (zkfp.ZKFP_ERR_OK == (ret = zkfp2.DBAdd(mDBHandle, Fid, CapTmp)))
                {
                    Message = $"注册成功";
                    IsRegister = false;
                    return;
                }
                else
                {
                    Message = $"注册失败,error code={ret}";
                }
                IsRegister = false;
                return;
            }
        }
        private void DoCapture()
        {
            if (!open)
            {
                Message = $"指纹模块打开失败";
                return;
            }

            while (!bIsTimeToDie)
            {
                cbCapTmp = 2048;
                int ret = zkfp2.AcquireFingerprint(mDevHandle, FPBuffer, CapTmp, ref cbCapTmp);
                if (ret == zkfp.ZKFP_ERR_OK)
                {
                    MemoryStream ms = new MemoryStream();
                    BitmapFormat.GetBitmap(FPBuffer, mfpWidth, mfpHeight, ref ms);
                    Bitmap bmp = new Bitmap(ms);
                    ImageChangedEvent?.Invoke(bmp);

                    registerOne();
                    //register();

                    identify();

                    verify();
                }
                Thread.Sleep(500);
            }
        }
        /// <summary>
        /// 登记
        /// </summary>
        public void Enroll()
        {
            TouchId = string.Empty;
            if (!open) return;
            bIdentify = false;
            IsRegister = true;
            RegisterCount = 0;
            cbRegTmp = 0;
            for (int i = 0; i < 3; i++)
            {
                RegTmps[i] = new byte[2048];
            }
            Message = $"请按手指!开始注册指纹";
        }
        //关闭
        public void Close()
        {
            bIsTimeToDie = true;
            Thread.Sleep(100);
            zkfp2.CloseDevice(mDevHandle);
        }
        /// <summary>
        /// 验证
        /// </summary>
        private void verify()
        {
            //int ret = zkfp2.DBMatch(mDBHandle, CapTmp, RegTmp);
            //if (0 < ret)
            //{
            //    Console.WriteLine("匹配指纹成功, score=" + ret + "!");
            //    return;
            //}
            //else
            //{
            //    Console.WriteLine("匹配指纹失败, ret= " + ret);
            //    return;
            //}
        }
        /// <summary>
        /// 身份识别
        /// </summary>
        public void Identify()
        {
            bIdentify = true;
        }
        /// <summary>
        /// 身份识别
        /// </summary>
        private void identify()
        {
            if (bIdentify)
            {
                int ret = zkfp.ZKFP_ERR_OK;
                int fid = 0, score = 0;
                ret = zkfp2.DBIdentify(mDBHandle, CapTmp, ref fid, ref score);
                if (zkfp.ZKFP_ERR_OK == ret)
                {
                    string msg = $"识别成功,fid={fid},score={score}";
                    Message = $"识别成功" + fid;
                    return;
                }
                else
                {
                    Message = $"未注册,ret={ret}";
                    return;
                }
            }
        }
        public void Dispose()
        {
            zkfp2.Terminate();
            cbRegTmp = 0;
        }
    }
}
